<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="错误处理（包括日志记录）">
<meta name="twitter:description" content="<p>错误处理的5个场景：将错误传送下去，尝试重试（指数退避策略），输出日志并退出，记录log日志（继续运行），忽略错误；error接口：errors包，fmt.Errorf，自定义错误；异常和捕捉</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gopl-error-handling.html"/>
<meta property="og:title" content="来玩魔王的咚 | 错误处理（包括日志记录）" />
<meta property="og:description" content="<p>错误处理的5个场景：将错误传送下去，尝试重试（指数退避策略），输出日志并退出，记录log日志（继续运行），忽略错误；error接口：errors包，fmt.Errorf，自定义错误；异常和捕捉</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>错误处理的5个场景：将错误传送下去，尝试重试（指数退避策略），输出日志并退出，记录log日志（继续运行），忽略错误；error接口：errors包，fmt.Errorf，自定义错误；异常和捕捉</p>" />

    <meta name="tags" content="gopl" />
    <meta name="tags" content="errors" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-07-15 15:30:00+08:00">
                <script>document.write(moment('2020-07-15 15:30:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-07-15 15:30:00+08:00">
                <script>document.write(moment('2020-07-16 16:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            错误处理（包括日志记录）
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#cuo-wu-chu-li">错误处理</a><ul>
<li><a href="#yi-jiang-cuo-wu-chuan-di-xia-qu">一、将错误传递下去</a></li>
<li><a href="#er-chang-shi-zhong-shi">二、尝试重试</a></li>
<li><a href="#san-shu-chu-ri-zhi-bing-tui-chu">三、输出日志并退出</a></li>
<li><a href="#si-ji-lu-log-ri-zhi">四、记录log日志</a></li>
<li><a href="#wu-hu-lve-cuo-wu">五、忽略错误</a></li>
</ul>
</li>
<li><a href="#error-jie-kou">error 接口</a><ul>
<li><a href="#errors-bao">errors 包</a></li>
<li><a href="#fmterrorf">fmt.Errorf</a></li>
<li><a href="#zi-ding-yi-cuo-wu">自定义错误</a></li>
</ul>
</li>
<li><a href="#yi-chang-he-bu-zhuo">异常和捕捉</a></li>
</ul>
</div>
<h3 id="cuo-wu-chu-li"><a class="toclink" href="#cuo-wu-chu-li">错误处理</a></h3>
<p>简单的错误处理是使用 Fprintf 和 %v 在标准错误流上输出一条消息，%v 可以使用默认格式显示任意类型的值。<br>
<em>Go1.13引入了wrapping error，增加了新的%w嵌套追加错误信息，所以现在推荐用%w。</em><br>
为了保持示例代码简短，有时会对错误处理有意进行一定程度的忽略。明显的错误还是要处理的。但是有些出现概率很小的错误，就忽略了，不过要标记所跳过的错误检查，就是加上注释。  </p>
<p>根据情形，将有许多可能的处理场景，接下来是5个例子。  </p>
<h4 id="yi-jiang-cuo-wu-chuan-di-xia-qu"><a class="toclink" href="#yi-jiang-cuo-wu-chuan-di-xia-qu">一、将错误传递下去</a></h4>
<p>最常见的情形是将错误传递下去，使得在子例程中发生的错误变为主调例程的错误。<br>
一种是不做任何操作立即向调用者返回错误：</p>
<div class="highlight"><pre><span></span><code><span class="nx">resp</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">Get</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>还有一种，不会直接返回，因为错误信息中缺失一些关键信息：</p>
<div class="highlight"><pre><span></span><code><span class="nx">doc</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">html</span><span class="p">.</span><span class="nx">Parse</span><span class="p">(</span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span><span class="w"></span>
<span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nx">Close</span><span class="p">()</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;parsing %s as HTML: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">  </span><span class="c1">// Go1.13后err用%w</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里格式化了一条错误消息并且返回一个新的错误值。可以为原始的错误消息不断地添加上下文信息来建立一个可读的错误描述。当错误最终被程序的 main 函数处理时，它应该能够提供一个从最根本问题到总体故障的清晰因果链。<br>
这里有一个 NASA 的事故调查的例子：</p>
<div class="highlight"><pre><span></span><code>genesis: crashed: no parachute: G-switch failed: bad relay orientation
</code></pre></div>

<p>因为错误频繁地串联起来，所以消息字符串首字母不应该大写而且应该避免换行。错误结果可能会很长，但能能够使用 grep 这样的工具找到需要的信息。  </p>
<p><strong>需要添加的关键信息</strong><br>
有时候可以不用添加信息直接返回，有时候需要添加一些关键信息，因为错误信息里没有。比如 os.Open 打开文件时，返回的错误不仅仅包括错误的信息，还包含文件的名字，因此调用者构造错误消息的时候不需要包含文件的名字这类信息。具体哪些信息是缺少的关键信息需要在原始的错误消息的基础上添加？<br>
一般地，f(x) 调用只负责报告函数的行为 f 和参数值 x，因为它们和错误的上下文相关。调用者则负责添加进一步的信息，但是 f(x) 本身并不会，并且在函数内部也没有这些信息。<br>
比如上面的 html.Parse 返回的错误信息里不可能有 url 的信息，但是，是关键信息需要添加。而 os.Open 中，文件名字也是关键信息，但是这个正是函数的参数值，所以函数本身会返回这个信息，不需要另外添加。  </p>
<h4 id="er-chang-shi-zhong-shi"><a class="toclink" href="#er-chang-shi-zhong-shi">二、尝试重试</a></h4>
<p>对于不固定或者不可预测的错误，在短暂的间隔后对操作进行重试是合乎情理的。超出一定的重试次数和限定的时间后再报错退出。<br>
下面给出了完整的代码，暂时只看 WaitForServer 函数：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;log&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;net/http&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;os&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;time&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="c1">// 尝试连接 url 对应的服务器</span><span class="w"></span>
<span class="c1">// 在一分钟内使用指数退避策略进行重试</span><span class="w"></span>
<span class="c1">// 所有的尝试失败后返回错误</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">WaitForServer</span><span class="p">(</span><span class="nx">url</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">const</span><span class="w"> </span><span class="nx">timeout</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Minute</span><span class="w"></span>
<span class="w">    </span><span class="nx">deadline</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">().</span><span class="nx">Add</span><span class="p">(</span><span class="nx">timeout</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">tries</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">().</span><span class="nx">Before</span><span class="p">(</span><span class="nx">deadline</span><span class="p">);</span><span class="w"> </span><span class="nx">tries</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">Head</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="c1">// 成功</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">log</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;server not responding (%s); retrying...&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="nb">uint</span><span class="p">(</span><span class="nx">tries</span><span class="p">))</span><span class="w"> </span><span class="c1">// 指数退避策略</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;server %s failed to respond after %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">,</span><span class="w"> </span><span class="nx">timeout</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;需要提供 url 参数\n&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">url</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">WaitForServer</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Site is down: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里的<strong>指数退避策略</strong>，以及尝试多次简单的超时退出的实现也很有意思。  </p>
<h4 id="san-shu-chu-ri-zhi-bing-tui-chu"><a class="toclink" href="#san-shu-chu-ri-zhi-bing-tui-chu">三、输出日志并退出</a></h4>
<p>接着看上面的代码，如果多次重试后依然不能成功，调用者能够输出错误然后优雅地停止程序。<br>
但一般这样的处理应该留给主程序部分：</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">WaitForServer</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Site is down: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>通常，如果是库函数，应该将错误传递给调用者，除非这个错误表示一个内部的一致性错误，这意味着库内部存在 bug。<br>
这里还有一个更加方便的方法是通过调用 log.Fatalf 实现上面相同的效果。<br>
和所有的日志函数一样，它默认会将时间和日期作为前缀添加到错误消息前：</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">WaitForServer</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">log</span><span class="p">.</span><span class="nx">Fatalf</span><span class="p">(</span><span class="s">&quot;Site is down: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这种带日期时间的默认格式有助于长期运行的服务器，而对于交互式的命令行工具则意义不大。  </p>
<p>还可以自定义命令的名称作为 log 包的前缀，并且将日期和时间略去：</p>
<div class="highlight"><pre><span></span><code><span class="nx">log</span><span class="p">.</span><span class="nx">SetPrefix</span><span class="p">(</span><span class="s">&quot;wait: &quot;</span><span class="p">)</span><span class="w"></span>
<span class="nx">log</span><span class="p">.</span><span class="nx">SetFlags</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="w"></span>
</code></pre></div>

<h4 id="si-ji-lu-log-ri-zhi"><a class="toclink" href="#si-ji-lu-log-ri-zhi">四、记录log日志</a></h4>
<p>在一些错误情况下，只记录下错误信息然后程序继续运行。<br>
同样地，可以选择使用 log 包来增加日志的常用前缀：</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Ping</span><span class="p">():</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">log</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;Ping failed: %v; networking disabled&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>所有 log 函数都会为缺少换行符的日志补充一个换行符。  </p>
<p>或者是，直接输出到标准错误流：</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Ping</span><span class="p">():</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Ping failed: %v; networking disabled\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>没有用 log 函数，所以没有时间日期，当然也不需要。上面说了，对于交互式的命令工具意义不大。  </p>
<h4 id="wu-hu-lve-cuo-wu"><a class="toclink" href="#wu-hu-lve-cuo-wu">五、忽略错误</a></h4>
<p>在某些罕见的情况下，还可以直接安全地忽略掉整个日志：</p>
<div class="highlight"><pre><span></span><code><span class="nx">dir</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ioutil</span><span class="p">.</span><span class="nx">TempDir</span><span class="p">(</span><span class="s">&quot;&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;scratch&quot;</span><span class="p">)</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;failed to create temp dir: %v&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="c1">// 使用临时的目录</span><span class="w"></span>
<span class="nx">os</span><span class="p">.</span><span class="nx">RemoveAll</span><span class="p">(</span><span class="nx">dir</span><span class="p">)</span><span class="w">  </span><span class="c1">// 忽略错误，$TMPDIR 会被周期性删除</span><span class="w"></span>
</code></pre></div>

<p>调用 os.RemoveAll 可能会失败，但程序忽略了这个错误，原因是操作系统会周期性地清理临时目录。在这个例子中，有意的抛弃了错误，但程序的逻辑看上去就和忘记去处理一样了。要习惯考虑到每一个函数调用可能发生的出错情况，当有意忽略一个错误的时候，要清楚地注释一下你的意图。  </p>
<h3 id="error-jie-kou"><a class="toclink" href="#error-jie-kou">error 接口</a></h3>
<p>之前已经使用过 error 类型了，实际上它是一个接口类型，包含一个返回错误消息的 Error() 方法。<br>
具体是在 builtin 包里定义的:</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>所以其实 error 也就是个字符串信息。</p>
<h4 id="errors-bao"><a class="toclink" href="#errors-bao">errors 包</a></h4>
<p>构造 error 最简单的方法是调用 errors.New，它会返回一个包含指定错误消息的新 error 实例。<br>
完整的 errors 包其实只有如下的4行代码：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">errors</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">New</span><span class="p">(</span><span class="nx">text</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">errorString</span><span class="p">{</span><span class="nx">text</span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">errorString</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">errorString</span><span class="p">)</span><span class="w"> </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">s</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>底层的 errorString 类型是一个结构体，而不是像其他包里那样定义字符串的别名类型。这主要是为了保护它所表示的错误值无意间的（或者也可能是故意的）更新。  </p>
<p>定义的 Error 方法是指针方法，而不是值方法。这样每次 New 分配的 error 实例都互不相等。<br>
即使是同样的错误值，也是不同的地址：</p>
<div class="highlight"><pre><span></span><code><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s">&quot;TEST&quot;</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s">&quot;TEST&quot;</span><span class="p">))</span><span class="w"> </span><span class="c1">// false</span><span class="w"></span>
</code></pre></div>

<p>这样可以避免比如像 io.EOF 这样重要的错误，与仅仅只是包含同样错误消息的一个错误相等。  </p>
<p>定义一个错误的示例如下，New 返回一个按给定文本格式化的错误：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;errors&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">errNotFound</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s">&quot;Not found error&quot;</span><span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;ERROR:&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">errNotFound</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里是先定义一个错误，然后再调用这个错误。先定义好错误，方便在多个地方复用。<br>
不考虑复用的话，也可以直接 errors.New。比如下一节开始的例子。  </p>
<h4 id="fmterrorf"><a class="toclink" href="#fmterrorf">fmt.Errorf</a></h4>
<p>直接调用 errors.New 的情况比较少，只在直接能取得错误值的字符串信息的时候使用：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">startCPUProfile</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">w</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s">&quot;nil File&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">pprof</span><span class="p">.</span><span class="nx">StartCPUProfile</span><span class="p">(</span><span class="nx">w</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>更多的情况是会得到一个错误值 err，而我们可以在这个错误值之上做一点包装，还需要做字符串格式化。有一个更易用的封装函数 fmt.Errorf，它额外还提供了字符串格式化的功能。<br>
所以一般都是用这个：</p>
<div class="highlight"><pre><span></span><code><span class="nx">doc</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">html</span><span class="p">.</span><span class="nx">Parse</span><span class="p">(</span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;parseing %s as HTML: %v&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="zi-ding-yi-cuo-wu"><a class="toclink" href="#zi-ding-yi-cuo-wu">自定义错误</a></h4>
<p>主要是学习，上面的 errors.New 用起来更加方便。<br>
使用自定义错误返回：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;os&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">PathError</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Op</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Path</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">err</span><span class="w"> </span><span class="kt">string</span><span class="w">  </span><span class="c1">// 把这个信息隐藏起来，所以是小写</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 实现error的接口</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">PathError</span><span class="p">)</span><span class="w"> </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Op</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Path</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; 路径不存在\n原始错误信息: &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">err</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">Open</span><span class="p">(</span><span class="nx">filename</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">file</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Open</span><span class="p">(</span><span class="nx">filename</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">PathError</span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">Op</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;read&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="nx">Path</span><span class="p">:</span><span class="w"> </span><span class="nx">filename</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="nx">err</span><span class="p">:</span><span class="w"> </span><span class="nx">err</span><span class="p">.</span><span class="nx">Error</span><span class="p">(),</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">file</span><span class="p">.</span><span class="nx">Close</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Open</span><span class="p">(</span><span class="s">&quot;test.txt&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="cm">/* 执行结果</span>
<span class="cm">PS H:\Go\src\go_dev\day7\error\diy_error&gt; go run main.go</span>
<span class="cm">read test.txt 路径不存在</span>
<span class="cm">原始错误信息: open test.txt: The system cannot find the file specified.</span>
<span class="cm">PS H:\Go\src\go_dev\day7\error\diy_error&gt;</span>
<span class="cm">*/</span><span class="w"></span>
</code></pre></div>

<h5 id="pan-duan-zi-ding-yi-cuo-wu"><a class="toclink" href="#pan-duan-zi-ding-yi-cuo-wu">判断自定义错误</a></h5>
<p>这里用 switch 来判断：</p>
<div class="highlight"><pre><span></span><code><span class="k">switch</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">err</span><span class="p">.(</span><span class="kd">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="k">case</span><span class="w"> </span><span class="nx">ParseError</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="nx">PrintParseError</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="k">case</span><span class="w"> </span><span class="nx">PathError</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="nx">PrintPathError</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="k">default</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h3 id="yi-chang-he-bu-zhuo"><a class="toclink" href="#yi-chang-he-bu-zhuo">异常和捕捉</a></h3>
<p>首先调用 panic 来抛出异常：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">badCall</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nb">panic</span><span class="p">(</span><span class="s">&quot;bad end&quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">badCall</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="cm">/* 执行结果</span>
<span class="cm">PS H:\Go\src\go_dev\day7\error\panic&gt; go run main.go</span>
<span class="cm">panic: bad end</span>

<span class="cm">goroutine 1 [running]:</span>
<span class="cm">main.badCall()</span>
<span class="cm">        H:/Go/src/go_dev/day7/error/panic/main.go:4 +0x40</span>
<span class="cm">main.main()</span>
<span class="cm">        H:/Go/src/go_dev/day7/error/panic/main.go:8 +0x27</span>
<span class="cm">exit status 2</span>
<span class="cm">PS H:\Go\src\go_dev\day7\error\panic&gt;</span>
<span class="cm">*/</span><span class="w"></span>
</code></pre></div>

<p>执行后就抛出异常了，但是这样程序也崩溃了。<br>
下面来捕获异常，go里没有try之类来捕获异常，所以panic了就是真的异常了，但是还不会马上就崩溃。panic的函数并不会立刻返回，而是先defer，再返回。如果有办法将panic捕获到，并阻止panic传递，就正常处理，如果没有没有捕获，程序直接异常终止。<em>这里并不是像别的语言里那样捕获异常，因为即使捕获到了，也只是执行defer，之后还是要异常终止的，而不是继续在错误的点往下执行。</em><br>
<strong>注意：</strong>就像上面说的，在go里panic了就是真的异常了。recover之后，逻辑并不会恢复到panic那个点去，函数还是会在defer之后返回。  </p>
<p>下面是使用 defer 处理异常的示例：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">badCall</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nb">panic</span><span class="p">(</span><span class="s">&quot;bad end&quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">test</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 用defer在退出前捕获异常</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">recover</span><span class="p">();</span><span class="w"> </span><span class="nx">e</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;Panic&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="nx">badCall</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">test</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>在go里，错误的处理，大概就是返回个 err(error类型) ，然后判断一下 err 是不是 nil。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>